package com.hmy.finance.job.core;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hmy.finance.api.bo.core.request.enums.FeeTypeEnum;
import com.hmy.finance.common.enums.BusinessBillTypeEnum;
import com.hmy.finance.common.enums.ProfitLossStatusEnum;
import com.hmy.finance.dal.mapper.DeviceProfitLossDetailMapper;
import com.hmy.finance.dal.mapper.DeviceRentalRecordMapper;
import com.hmy.finance.dal.mapper.ProfitLossDailyLogMapper;
import com.hmy.finance.dal.mapper.ProfitLossRecordMapper;
import com.hmy.finance.dal.po.DeviceProfitLossDetailPo;
import com.hmy.finance.dal.po.DeviceRentalRecordPo;
import com.hmy.finance.dal.po.ProfitLossDailyLogPo;
import com.hmy.finance.dal.po.ProfitLossRecordPo;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 损益日流水生成job处理器
 *
 * @author songkf
 */
@Slf4j
@Component
public class ProfitLossDailyGenerateJobHandler {
    @Resource
    private DefaultIdentifierGenerator identifierGenerator;
    /**
     * 损益业务记录mapper
     */
    @Resource
    private ProfitLossRecordMapper profitLossRecordMapper;
    /**
     * 设备损益明细记录mapper
     */
    @Resource
    private DeviceProfitLossDetailMapper deviceProfitLossDetailMapper;
    /**
     * 设备租赁记录mapper
     */
    private DeviceRentalRecordMapper deviceRentalRecordMapper;
    /**
     * 损益每日处理日志mapper
     */
    private ProfitLossDailyLogMapper profitLossDailyLogMapper;

    /**
     * 生成损益日流水
     *
     * @author songkf
     */

    @XxlJob("financial_generateProfitLossDaily")
    public void generateProfitLossDaily() {
        List<DeviceProfitLossDetailPo> deviceProfitLossDetailList = new ArrayList<>();
        //已处理的损益业务记录集合
        List<Long> processedProfitLossRecordIdList = new ArrayList<>();
        log.info("开始执行损益日流水生成job");
        // 1.查询需要生成损益日流水的业务记录
        // 1.1 使用Wrappers构建状态为待生效的且费用发生日期等于今天的损益业务记录的LambdaQueryWrapper
        List<ProfitLossRecordPo> profitLossRecordList = profitLossRecordMapper.selectList(Wrappers.lambdaQuery(ProfitLossRecordPo.class)
                .eq(ProfitLossRecordPo::getStatus, ProfitLossStatusEnum.WAIT_EFFECTIVE.getValue())
                .le(ProfitLossRecordPo::getFeeIncurredTime, LocalDate.now()));
        log.info("损益日流水生成job查询到的待生效的损益业务记录数量为：{}", profitLossRecordList.size());
        log.info("损益日流水生成job查询到的待生效的损益业务记录为：{}", JSON.toJSONString(profitLossRecordList));
        //1.2 遍历profitLossRecordList，根据ProfitLossRecordPo的单据类型和费用类型，生成设备损益明细记录
        profitLossRecordList.forEach(profitLossRecord -> {
            // 1.2.1 根据profitLossRecord的单据类型和费用类型，生成设备损益明细记录
            if (Objects.requireNonNull(profitLossRecord.getBusinessBillType()) == BusinessBillTypeEnum.ENTRY) {//获取进场单ID关联的设备租赁记录
                List<DeviceRentalRecordPo> deviceRentalRecordPoList = deviceRentalRecordMapper.selectList(Wrappers.lambdaQuery(DeviceRentalRecordPo.class)
                        .eq(DeviceRentalRecordPo::getEntryFormId, profitLossRecord.getBusinessBillId())
                        //只查询退场单ID为空的
                        .isNull(DeviceRentalRecordPo::getExitFormId)
                        .isNull(DeviceRentalRecordPo::getRentalEndTime)
                );
                deviceProfitLossDetailList.addAll(assembleDeviceProfitLossDetail(profitLossRecord.getId(), profitLossRecord.getTotalFeeAmount(), profitLossRecord.getFeeType(), deviceRentalRecordPoList));
                processedProfitLossRecordIdList.add(profitLossRecord.getId());
                log.info("损益日流水生成job基于损益业务记录:{},生成出的设备分摊损益明细记录是:{}", JSON.toJSONString(profitLossRecord), JSON.toJSONString(deviceProfitLossDetailList));
            }
        });
        // 2.批量插入设备损益明细记录
        if (!deviceProfitLossDetailList.isEmpty()) {
            log.info("最终处理的设备损益明细记录为：{}", JSON.toJSONString(deviceProfitLossDetailList));
            int batch = deviceProfitLossDetailMapper.insertBatch(deviceProfitLossDetailList);
            log.info("损益日流水生成job插入设备损益明细记录数量为：{}", batch);
        }
        // 3.更新损益业务记录状态为已生效
        if (!processedProfitLossRecordIdList.isEmpty()) {
            log.info("损益日流水生成job更新损益业务记录状态为已生效的损益业务记录ID集合为：{}", JSON.toJSONString(processedProfitLossRecordIdList));
            LambdaUpdateWrapper<ProfitLossRecordPo> lambdaUpdateWrapper = Wrappers.lambdaUpdate(ProfitLossRecordPo.class)
                    .in(ProfitLossRecordPo::getId, processedProfitLossRecordIdList);
            ProfitLossRecordPo po = new ProfitLossRecordPo();
            po.setStatus(ProfitLossStatusEnum.ALREADY_EFFECTIVE);
            int updated = profitLossRecordMapper.update(po, lambdaUpdateWrapper);
            log.info("损益日流水生成job更新损益业务记录状态为已生效的损益业务记录数量为：{}", updated);
        }
        //4. 记录损益生成日志
        //4.1 构建损益日流水生成日志
        ProfitLossDailyLogPo profitLossDailyLogPo = new ProfitLossDailyLogPo();
        profitLossDailyLogPo.setIncurredTime(LocalDateTime.now());
        profitLossDailyLogPo.setRelatedProfitLossRecordIds(processedProfitLossRecordIdList);
        //4.2 插入损益日流水生成日志
        log.info("损益日流水生成job插入损益日流水生成日志的参数为：{}", JSON.toJSONString(profitLossDailyLogPo));
        profitLossDailyLogMapper.insert(profitLossDailyLogPo);
        log.info("损益日流水生成job执行完成");
    }

    public List<DeviceProfitLossDetailPo> assembleDeviceProfitLossDetail(Long profitLossRecordId, BigDecimal totalAmount, FeeTypeEnum businessBillType, List<DeviceRentalRecordPo> rentingDeviceList) {
        List<DeviceProfitLossDetailPo> currentDeviceProfitLossDetailPoList = new ArrayList<>(rentingDeviceList.size());
        for (int i = 0; i < rentingDeviceList.size(); i++) {
            DeviceRentalRecordPo deviceRentalRecordPo = rentingDeviceList.get(i);
            DeviceProfitLossDetailPo deviceProfitLossDetailPo = new DeviceProfitLossDetailPo();
            deviceProfitLossDetailPo.setProfitLossRecordId(profitLossRecordId);
            deviceProfitLossDetailPo.setDeviceId(deviceRentalRecordPo.getDeviceId());
            deviceProfitLossDetailPo.setDeviceNo(deviceRentalRecordPo.getDeviceExtensionInfo().getDeviceNo());
            deviceProfitLossDetailPo.setContractId(deviceRentalRecordPo.getContractId());
            //根据totalAmount进行rentingDeviceList的设备分摊，最后一个设备的分摊金额为totalAmount减去前面设备的分摊金额之和
            if (i == rentingDeviceList.size() - 1) {
                deviceProfitLossDetailPo.setFee(totalAmount.subtract(currentDeviceProfitLossDetailPoList.stream().map(DeviceProfitLossDetailPo::getFee).reduce(BigDecimal.ZERO, BigDecimal::add)));
            } else {
                deviceProfitLossDetailPo.setFee(totalAmount.divide(BigDecimal.valueOf(currentDeviceProfitLossDetailPoList.size()), 5, RoundingMode.HALF_UP));
            }

            deviceProfitLossDetailPo.setFeeType(businessBillType);
            deviceProfitLossDetailPo.setDeviceRentalRecordId(deviceRentalRecordPo.getId());
            deviceProfitLossDetailPo.setFeeIncurredTime(LocalDateTime.now());
            deviceProfitLossDetailPo.setId(identifierGenerator.nextId(deviceProfitLossDetailPo));
            currentDeviceProfitLossDetailPoList.add(deviceProfitLossDetailPo);
        }
        return currentDeviceProfitLossDetailPoList;
    }
}
